using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Soopah.Xna.Input;
using System.IO;
using System.Xml.Serialization;
namespace XNADirectInput
{
    public class XDI : GameComponent
    {
        GamePadState State;
        DirectInputGamepad gp;
        public Dictionary<string,ButtonSettings> bSettings;
        List<ButtonSettings> LoadedSettings;


        /// <summary>
        /// The collection of connected gamepads
        /// </summary>
        /// <param name="index">The gamepads 0 based index</param>
        /// <returns></returns>
        public DirectInputGamepad this[int index]
        {
            get { return DirectInputGamepadManager.Gamepads[index]; }
        }

        /// <summary>
        /// Count of connected Gamepads
        /// </summary>
        public int Count
        {
            get { return DirectInputGamepadManager.Gamepads.Count; }
        }

        public XDI(Game game)
            : base(game)
        {
            State = new GamePadState();
            bSettings = new Dictionary<string,ButtonSettings>();
        }

        /// <summary>
        /// Gets et's the state of the selected GamePad
        /// </summary>
        /// <param name="PadIndex">This is the index of the GamePad, NOT the Player Index</param>
        /// <returns>XNA GamePad state if the pad exists, else an Empty pad state</returns>
        public GamePadState GetState(int PadIndex)
        {
            if (DirectInputGamepadManager.Gamepads[PadIndex] != null)
            {
                gp = DirectInputGamepadManager.Gamepads[PadIndex];
                State = new GamePadState(gp.ThumbSticks.Left, gp.ThumbSticks.Right, gp.ThumbSticks.Third.X, gp.ThumbSticks.Third.Y, gp.Buttons.OnButtons.ToArray());
                return State;
            }
            State = new GamePadState();
            return State;
        }

        /// <summary>
        /// Attempts to read all files in the directory specified.
        /// If the file is a ButtonSettings File, and it does not already exist
        /// it is added to the collection of settings.
        /// </summary>
        /// <param name="PathToDeviceFiles"></param>
        public void LoadSettings(string PathToDeviceFiles)
        {
            XmlSerializer xml = new XmlSerializer(typeof(ButtonSettings));
            DirectoryInfo di = new DirectoryInfo(PathToDeviceFiles);
            foreach (FileInfo f in di.GetFiles())
            {
                try
                {
                    ButtonSettings bs = (ButtonSettings)xml.Deserialize(f.OpenRead());
                    if(!bSettings.ContainsKey(bs.Name))
                        bSettings.Add(bs.Name,bs);
                }
                catch (InvalidOperationException)
                {
                    //Eat any InvalidOperation exceptions. 
                    //These are caused by trying to deserialize files that aren't buttonSettings files
                }
            }
        }

        /// <summary>
        /// Reloads the static GamePads collection
        /// Note that any manually assigned settings 
        /// will need to be re-assigned after calling this.
        /// </summary>
        public void LoadGamePads()
        {
            DirectInputGamepadManager.ReloadGamepads();
            foreach (DirectInputGamepad tgp in DirectInputGamepadManager.Gamepads)
            {
                ButtonSettings bs;
                if(bSettings.TryGetValue(tgp.PadName,out bs))
                {
                    tgp.bSettings = bs;
                }
            }
        }

        /// <summary>
        /// Reads in settings rom the content pipeline.
        /// Use this method if you have some default controller settings baked in.
        /// </summary>
        /// <param name="settingsName"></param>
        public void LoadFromContent(string settingsName)
        {
            LoadedSettings = Game.Content.Load<List<ButtonSettings>>(settingsName);
            foreach (ButtonSettings b in LoadedSettings)
            {
                if (!bSettings.ContainsKey(b.Name))
                    bSettings.Add(b.Name, b);
            }
        }

        /// <summary>
        /// Returns a list of controller names
        /// </summary>
        /// <returns></returns>
        public List<string> GetControllerNames()
        {
            List<string> lst = new List<string>();
            for (int i = 0; i < DirectInputGamepadManager.Gamepads.Count; i++)
            {
                lst.Add(DirectInputGamepadManager.Gamepads[i].PadName.Trim());
            }
            return lst;
        }

        /// <summary>
        /// Get's the name of a Pad at the selected index or "" if no pad at that index
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public string GetPadName(int index)
        {
            return this[index] == null ? "" : this[index].PadName;
        }

        /// <summary>
        /// Returns a collection of the setting Names;
        /// </summary>
        /// <returns></returns>
        public List<string> GetSettingNames()
        {
            return bSettings.Keys.ToList();
        }

        /// <summary>
        /// Sets the settings file of a given pad to a given settings file
        /// </summary>
        /// <param name="PadIndex">Gamepad to assign the settings to</param>
        /// <param name="SettingsIndex">The Settings file to apply</param>
        public void SetSettings(int PadIndex, string SettingsIndex)
        {
            if(this[PadIndex] != null)
                this[PadIndex].bSettings = bSettings[SettingsIndex];
        }

        /// <summary>
        /// Polls all currently connected devices.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            DirectInputGamepadManager.PollDevices();
            base.Update(gameTime);
        }
    }
}
